#include "OLED.h"
#include "spi.h"
#include "oledfont.h"  


#define SPI_DMA 1  // 使用SPI_DMA

// 计算帧率
uint16_t OLED_GetFPS(void)
{
	static uint32_t tick_start = 0;
	static uint32_t count = 0;
	static uint16_t FPS = 0;

	count++;
	if (count >= 30)
	{
		FPS = count * 1000 / (HAL_GetTick() - tick_start);
		count = 0;
		tick_start = HAL_GetTick();
	}
	return FPS;
}

//反显函数
void OLED_ColorTurn(uint8_t i)
{
	if(i==0)
		{
			OLED_SendCmd(0xA6);//正常显示
		}
	if(i==1)
		{
			OLED_SendCmd(0xA7);//反色显示
		}
}

//屏幕旋转180度
void OLED_DisplayTurn(uint8_t i)
{
	if(i==0)
		{
			OLED_SendCmd(0xC8);//正常显示
			OLED_SendCmd(0xA1);
		}
	if(i==1)
		{
			OLED_SendCmd(0xC0);//反转显示
			OLED_SendCmd(0xA0);
		}
}


//取代OLED_WR_Byte函数，只写命令
void OLED_SendCmd(uint8_t TxData)
{
	OLED_DC_Clr();//写命令
	OLED_CS_Clr();//拉低片选
	HAL_SPI_Transmit(&hspi1, &TxData, 1, 1000);
	OLED_CS_Set();//拉高片选
	OLED_DC_Set();//写数据
}

//开启OLED显示 
void OLED_DisPlay_On(void)
{
	OLED_SendCmd(0x8D);//电荷泵使能
	OLED_SendCmd(0x14);//开启电荷泵
	OLED_SendCmd(0xAF);//点亮屏幕
}

//关闭OLED显示 
void OLED_DisPlay_Off(void)
{
	OLED_SendCmd(0x8D);//电荷泵使能
	OLED_SendCmd(0x10);//关闭电荷泵
	OLED_SendCmd(0xAE);//关闭屏幕
}

//SPI更新显存到OLED	
void OLED_Refresh(void)
{
	uint8_t page, line = 0;
	
	OLED_DC_Set();//写数据
	
	// 使用页模式刷新OLED
	// for(page = 0; page < 8; page++)
	// {
	// 	OLED_SendCmd(0xb0 + page); 	//设置行起始地址
	// 	OLED_SendCmd(0x00);   	//设置低列起始地址
	// 	OLED_SendCmd(0x10);   	//设置高列起始地址
	// 	for(line = 0; line < 128; line++){
	// 		HAL_SPI_Transmit(&hspi1, &OLED_GRAM[line][page], 1, 1000);
	// 	}
	// }
	HAL_SPI_Transmit(&hspi1, &OLED_GRAM[0][0], 1024, 1000);
	OLED_DC_Set();//写数据
}



#ifdef SPI_DMA

void OLED_Clear(void)
{
	uint8_t page, line;
	for(page=0; page<8; page++)
	{
	   	for(line=0; line<128; line++)
		{
			OLED_GRAM[page][line] = 0x00;//清除所有数据
		}
  	}
	// OLED_Refresh();//更新显示
}

//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空
void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
	uint8_t i,m,n;
	i=y/8;
	m=y%8;
	n=1<<m;
	if(t){OLED_GRAM[i][x]|=n;}
	else
	{
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
		OLED_GRAM[x][i]|=n;
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
	}
}

#else

//清屏函数
void OLED_Clear(void)
{
	uint8_t i,n;
	for(i=0;i<8;i++)
	{
	   	for(n=0;n<128;n++)
		{
			OLED_GRAM[n][i] = 0x00;//清除所有数据
		}
  	}
	// OLED_Refresh();//更新显示
}
void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
	uint8_t i,m,n;
	i=y/8;
	m=y%8;
	n=1<<m;
	if(t){OLED_GRAM[x][i]|=n;}
	else
	{
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
		OLED_GRAM[x][i]|=n;
		OLED_GRAM[x][i]=~OLED_GRAM[x][i];
	}
}
#endif

void OLED_Show_Char(uint8_t ye,uint8_t lie, uint8_t c)
{
	uint8_t i=0;

	for (i=0;i<6;i++)
	{
		OLED_GRAM[lie+i][ye]=asc2_0806[c - ' '][i];
	}
}

//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t mode)
{
	uint16_t t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance;
	int incx,incy,uRow,uCol;
	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1;
	uRow=x1;//画线起点坐标
	uCol=y1;
	if(delta_x>0)incx=1; //设置单步方向 
	else if (delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;}
	if(delta_y>0)incy=1;
	else if (delta_y==0)incy=0;//水平线 
	else {incy=-1;delta_y=-delta_x;}
	if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y;
	for(t=0;t<distance+1;t++)
	{
		OLED_DrawPoint(uRow,uCol,mode);//画点
		xerr+=delta_x;
		yerr+=delta_y;
		if(xerr>distance)
		{
			xerr-=distance;
			uRow+=incx;
		}
		if(yerr>distance)
		{
			yerr-=distance;
			uCol+=incy;
		}
	}
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(uint8_t x,uint8_t y,uint8_t r)
{
	int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b,1);
        OLED_DrawPoint(x - a, y - b,1);
        OLED_DrawPoint(x - a, y + b,1);
        OLED_DrawPoint(x + a, y + b,1);
 
        OLED_DrawPoint(x + b, y + a,1);
        OLED_DrawPoint(x + b, y - a,1);
        OLED_DrawPoint(x - b, y - a,1);
        OLED_DrawPoint(x - b, y + a,1);
        
        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}



//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1,uint8_t mode)
{
	uint8_t i,m,temp,size2,chr1;
	uint8_t x0=x,y0=y;
	if(size1==8)size2=6;
	else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
	chr1=chr-' ';  //计算偏移后的值
	for(i=0;i<size2;i++)
	{
		if(size1==8)
			  {temp=asc2_0806[chr1][i];} //调用0806字体
		else if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
		else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
		else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
		else return;
		for(m=0;m<8;m++)
		{
			if(temp&0x01)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp>>=1;
			y++;
		}
		x++;
		if((size1!=8)&&((x-x0)==size1/2))
		{x=x0;y0=y0+8;}
		y=y0;
  }
}


//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t size1,uint8_t mode)
{
	while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
	{
		OLED_ShowChar(x,y,*chr,size1,mode);
		if(size1==8)x+=6;
		else x+=size1/2;
		chr++;
  	}
}

//m^n
uint32_t OLED_Pow(uint8_t m,uint8_t n)
{
	uint32_t result=1;
	while(n--)
	{
	  result*=m;
	}
	return result;
}

//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(uint8_t x,uint8_t y,uint32_t num,uint8_t len,uint8_t size1,uint8_t mode)
{
	uint8_t t,temp,m=0;
	if(size1==8)m=2;
	for(t=0;t<len;t++)
	{
		temp=(num/OLED_Pow(10,len-t-1))%10;
			if(temp==0)
			{
				OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
      		}
			else 
			{
			  	OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
			}
  	}
}

//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(uint8_t x,uint8_t y,uint8_t num,uint8_t size1,uint8_t mode)
{
	uint8_t m,temp;
	uint8_t x0=x,y0=y;
	uint16_t i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数
	for(i=0;i<size3;i++)
	{
		if(size1==16)
				{temp=Hzk1[num][i];}//调用16*16字体
		else if(size1==24)
				{temp=Hzk2[num][i];}//调用24*24字体
		else if(size1==32)       
				{temp=Hzk3[num][i];}//调用32*32字体
		else if(size1==64)
				{temp=Hzk4[num][i];}//调用64*64字体
		else return;
		for(m=0;m<8;m++)
		{
			if(temp&0x01)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp>>=1;
			y++;
		}
		x++;
		if((x-x0)==size1)
		{x=x0;y0=y0+8;}
		y=y0;
	}
}

//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(uint8_t num,uint8_t space,uint8_t mode)
{
	uint8_t i,n,t=0,m=0,r;
	while(1)
	{
		if(m==0)
		{
	    OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
			t++;
		}
		if(t==num)
			{
				for(r=0;r<16*space;r++)      //显示间隔
				 {
					for(i=1;i<144;i++)
						{
							for(n=0;n<8;n++)
							{
								OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
							}
						}
           OLED_Refresh();
				 }
        t=0;
      }
		m++;
		if(m==16){m=0;}
		for(i=1;i<144;i++)   //实现左移
		{
			for(n=0;n<8;n++)
			{
				OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
			}
		}
		OLED_Refresh();
	}
}

//x,y：起点坐标
//sizex,sizey,图片长宽
//BMP[]：要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(uint8_t x,uint8_t y,uint8_t sizex,uint8_t sizey,uint8_t BMP[],uint8_t mode)
{
	uint16_t j=0;
	uint8_t i,n,temp,m;
	uint8_t x0=x,y0=y;
	sizey=sizey/8+((sizey%8)?1:0);
	for(n=0;n<sizey;n++)
	{
		 for(i=0;i<sizex;i++)
		 {
				temp=BMP[j];
				j++;
				for(m=0;m<8;m++)
				{
					if(temp&0x01)OLED_DrawPoint(x,y,mode);
					else OLED_DrawPoint(x,y,!mode);
					temp>>=1;
					y++;
				}
				x++;
				if((x-x0)==sizex)
				{
					x=x0;
					y0=y0+8;
				}
				y=y0;
     	}
	}
}

// OLED的初始化，初始化GPIO、SPI、DMA
// 使用了GPIOA的Pin4、5、6、7、8
// PA4-SPI1_NSS		用作OLED的RES
// PA5-SPI1_SCLK	用作OLED的D0
// PA6-SPI1_MISO	用作OLED的DC
// PA7-SPI1_MOSI	用作OLED的D1
// PA8-CS


// 初始化SPI1_TX的DMA
// static void DMA_OLED_Init(void)
// {
// 	DMA_InitTypeDef DMA_InitStructure;
// 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 	DMA_DeInit(DMA1_Channel3);
// 	DMA_InitStructure.DMA_PeripheralBaseAddr =  (uint32_t)SPI1->DR;	//DMA外设基地址
// 	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)OLED_GRAM;		//DMA内存基地址
// 	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;				//从储存器读取发送到外设
// 	DMA_InitStructure.DMA_BufferSize = 1024;						//DMA缓存的大小
// 	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;			//内存地址递增
// 	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变
// 	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//8位
// 	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//8位
// 	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;					//工作模式 正常/循环传输
// 	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;			//DMA 通道 x 拥有中优先级
// 	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;					//非内存到内存传输
// 	DMA_Init(DMA1_Channel3, &DMA_InitStructure);

// 	DMA_Cmd(DMA1_Channel3, ENABLE);
// }

void OLED_Init(void)
{
	OLED_RES_Clr();
	HAL_Delay(200);
	OLED_RES_Set();

	OLED_SendCmd(0xae);//关闭显示
	OLED_SendCmd(0xd5);//设置时钟分频因子,震荡频率
	OLED_SendCmd(0x80);//[3:0],分频因子;[7:4],震荡频率
	
	OLED_SendCmd(0x81);//设置对比度
	OLED_SendCmd(0xcf);//128
	// OLED_SendCmd(0x8d);//设置电荷泵开关
	// OLED_SendCmd(0x14);//开

	OLED_SendCmd(0x20);//设置模式
	// OLED_SendCmd(0x02);//设置为页地址模式
	// OLED_SendCmd(0x0b);
	// OLED_SendCmd(0x00);
	// OLED_SendCmd(0x10);

	// OLED_SendCmd(0x40);//设置显示开始行 0-63  0100 0000--0x40(0)  0111 1111--0x7f(63)

	OLED_SendCmd(0x00);//设置为水平地址模式
	// OLED_SendCmd(0x01);//设置为垂直地址模式
    OLED_SendCmd(0x21);//设置列地址的起始和结束的位置
    OLED_SendCmd(0x00);//0
    OLED_SendCmd(0x7f);//127
    OLED_SendCmd(0x22);//设置页地址的起始和结束的位置
    OLED_SendCmd(0x00);//0
    OLED_SendCmd(0x07);//7

	OLED_SendCmd(0xc8);//0xc0上下反置 0xc8正常
    OLED_SendCmd(0xa1);//0xa0左右反置 0xa1正常
	
	OLED_SendCmd(0xa4);//全局显示开启;0xa4正常,0xa5无视命令点亮全屏
	OLED_SendCmd(0xa6);//设置显示方式;bit0:1,反相显示;0,正常显示	

	OLED_SendCmd(0xaf);//开启显示

	OLED_Clear();
	// DMA_OLED_Init();//DMA初始化
	
	// OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/ 
	// OLED_WR_Byte(0x02,OLED_CMD); /*set lower column address*/ 
	// OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/ 
	// OLED_WR_Byte(0x40,OLED_CMD); /*set display start line*/ 
	// OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
	// OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/ 
	// OLED_WR_Byte(0xcf,OLED_CMD); /*128*/ 
	// OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/ 
	// OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/ 
	// OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/ 
	// OLED_WR_Byte(0x3F,OLED_CMD); /*duty = 1/64*/ 
	// OLED_WR_Byte(0xad,OLED_CMD); /*set charge pump enable*/ 
	// OLED_WR_Byte(0x8b,OLED_CMD); /* 0x8B 内供 VCC */ 
	// OLED_WR_Byte(0x33,OLED_CMD); /*0X30---0X33 set VPP 9V */ 
	// OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/ 
	// OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/ 
	// OLED_WR_Byte(0x00,OLED_CMD); /* 0x20 */ 
	// OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/ 
	// OLED_WR_Byte(0x80,OLED_CMD); 
	// OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/ 
	// OLED_WR_Byte(0x1f,OLED_CMD); /*0x22*/ 
	// OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/ 
	// OLED_WR_Byte(0x12,OLED_CMD); 
	// OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/ 
	// OLED_WR_Byte(0x40,OLED_CMD);
	// OLED_Clear();
	// OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}

